Introduction

In this project we are trying first to explore our data to get a better understanding To answer our questions we first need to have a preliminary look at our data, so that we can get a better a idea what we are dealing with, as well as the possible missing data and relationships that exist

Preliminary Look at the data

We need first to define the data we have.

Variable Definition Key
survival Survival 0 = No, 1 = yes
pclass ticket class 1 = 1st, 2 = 2nd, 3 = 3rd
sex sex
age Age in year
sibsp Number of siblings/spouses aboard the titanic
parch Number of parents/children aboard the Titanic
ticket ticket number(unique)
fare Passenger fare
cabin Cabin number
embarked port of embarkation C = Cherbourg, Q = Queens-town, S = Southampton

Loading the packages and the Data

# Loading Packages
## tidyverse loads dplyr and readr
library(tidyverse)

## To have different color maps
library(viridis)

## ggplot2 to produce different plots
library(ggplot2)

## uses ggplot2 to produce a correlation matrix -- the data must be in the correct form
library(ggcorrplot)

## Gives us better themes
library(hrbrthemes)

## to use skewness fun. to calculate skewness of the distribution
library(e1071)

## Multivariate imputation using chained equations -- to impute the missing values in our data
library(mice)

## Loads different statistical functions
library(statsr)

## To produce interactive plot
library(plotly)


# Loading Training Data
train <- read_csv("data/train.csv")

# Loading Testing Data
test <- read_csv("data/test.csv")

# Binding them into a full data frame
df <- bind_rows(train,test)

Exploration Of The Data

Summary of Data

summary(train)
  PassengerId       Survived          Pclass          Name               Sex                 Age       
 Min.   :  1.0   Min.   :0.0000   Min.   :1.000   Length:891         Length:891         Min.   : 0.42  
 1st Qu.:223.5   1st Qu.:0.0000   1st Qu.:2.000   Class :character   Class :character   1st Qu.:20.12  
 Median :446.0   Median :0.0000   Median :3.000   Mode  :character   Mode  :character   Median :28.00  
 Mean   :446.0   Mean   :0.3838   Mean   :2.309                                         Mean   :29.70  
 3rd Qu.:668.5   3rd Qu.:1.0000   3rd Qu.:3.000                                         3rd Qu.:38.00  
 Max.   :891.0   Max.   :1.0000   Max.   :3.000                                         Max.   :80.00  
                                                                                        NA's   :177    
     SibSp           Parch           Ticket               Fare           Cabin          
 Min.   :0.000   Min.   :0.0000   Length:891         Min.   :  0.00   Length:891        
 1st Qu.:0.000   1st Qu.:0.0000   Class :character   1st Qu.:  7.91   Class :character  
 Median :0.000   Median :0.0000   Mode  :character   Median : 14.45   Mode  :character  
 Mean   :0.523   Mean   :0.3816                      Mean   : 32.20                     
 3rd Qu.:1.000   3rd Qu.:0.0000                      3rd Qu.: 31.00                     
 Max.   :8.000   Max.   :6.0000                      Max.   :512.33                     
                                                                                        
   Embarked        
 Length:891        
 Class :character  
 Mode  :character  
                   
                   
                   
                   

Categories of Features

Quantitative data are measures of values or counts and are expressed as numbers.

Qualitative data are measures of ‘types’ and may be represented by a name, symbol, or a number code.

Qualitive

Categorical: Survived, Sex, and Embarked. Ordinal: Pclass. Nominal: Name.

Quantitive

Continuous: Age, Fare. Discrete: SibSp, Parch.

Mix types

Ticket is a mix of numeric and alphanumeric data types Cabin is mix between alpha and numeric

Exploring Missing Data.

Checking for Missing values in each feature

colSums(is.na(train))
PassengerId    Survived      Pclass        Name         Sex         Age       SibSp       Parch 
          0           0           0           0           0         177           0           0 
     Ticket        Fare       Cabin    Embarked 
          0           0         687           2 
missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))

missing_values <- gather(missing_values, key="feature", value="missing_pct")

missing_values

missing_values %>% 
  ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
  geom_bar(stat="identity",fill="red") +
  coord_flip() + # to flip the graph
  xlab("Features") +
  ylab("Percentage of missing values")+
  scale_y_continuous(labels=scales::percent) +
  theme_ipsum()

Missing data, it is normal?

It is quite normal to see missing data in any data-set as such data is collected by manually which means that there might be some error. Missing data present various problems. First, the absence of data reduces statistical power, which refers to the probability that the test will reject the null hypothesis when it is false. Second, the lost data can cause bias in the estimation of parameters. Third, it can reduce the representativeness of the samples.

The solution to the missing data

Removal

The removal of the observations that contain missing might cause a bigger issue where it might an even bigger loss of information which cause bias in our estimation. But on close observation of our data we can identify that there are some features that are not extremely useful and contain missing data, so we can drop such features.

train <- train %>%
  select(!Cabin)
summary(train)
  PassengerId       Survived          Pclass          Name               Sex                 Age       
 Min.   :  1.0   Min.   :0.0000   Min.   :1.000   Length:891         Length:891         Min.   : 0.42  
 1st Qu.:223.5   1st Qu.:0.0000   1st Qu.:2.000   Class :character   Class :character   1st Qu.:20.12  
 Median :446.0   Median :0.0000   Median :3.000   Mode  :character   Mode  :character   Median :28.00  
 Mean   :446.0   Mean   :0.3838   Mean   :2.309                                         Mean   :29.70  
 3rd Qu.:668.5   3rd Qu.:1.0000   3rd Qu.:3.000                                         3rd Qu.:38.00  
 Max.   :891.0   Max.   :1.0000   Max.   :3.000                                         Max.   :80.00  
                                                                                        NA's   :177    
     SibSp           Parch           Ticket               Fare          Embarked        
 Min.   :0.000   Min.   :0.0000   Length:891         Min.   :  0.00   Length:891        
 1st Qu.:0.000   1st Qu.:0.0000   Class :character   1st Qu.:  7.91   Class :character  
 Median :0.000   Median :0.0000   Mode  :character   Median : 14.45   Mode  :character  
 Mean   :0.523   Mean   :0.3816                      Mean   : 32.20                     
 3rd Qu.:1.000   3rd Qu.:0.0000                      3rd Qu.: 31.00                     
 Max.   :8.000   Max.   :6.0000                      Max.   :512.33                     
                                                                                        

Imputation

Imputing the missing data gives the advantage that we use a learning model to predict such missing data and maintain the distribution of our data.

Histogram of Age feature.

gAgeDensity <- train %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 20,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) + 
  annotate(
    "text",
    x = 70,
    y = 0.04,
    label = paste("Skewness:",skewness(train$Age,na.rm = T)),
    colour = inferno(1,begin = 0.1),
    size = 4
  ) + 
  theme_ipsum_rc()

gAgeDensity

Population mean and standard deviation of Age feature Before imputation

train %>%
  summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))

Histogram of Fare feature

gFareDensity <- train %>%
  select(Fare) %>%
  ggplot(aes(Fare, y = ..density..)) +
  geom_histogram(bins = 20,binwidth = 1,color=viridis(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = viridis(1,begin=0)) + 
  scale_y_continuous(limits = c(0,0.05))+
  theme_ipsum_rc() + 
  annotate(
    "text",
    x = 200,
    y = 0.05,
    label = paste("Skewness",skewness(train$Fare)),
    colour = "black",
    size = 4
  )

gFareDensity 

Population mean and standard deviaiton of Fare feature.

train %>%
  summarise(Fare_mean = mean(Fare,na.rm = T), Fare_sd = sd(Fare,na.rm = T))

Imputing the missing age feature

Correlation matrix before imputation

We are going to use correlation matrix of the numerical data to assess the correlation, which might gives a better idea of which feature might be important

The fare features seems to be the most correlated feature to survival of the passengers, but it doesn’t negate the importance of the other features in the data. Which means that we will start by comparing the each that we consider to be important against survival feature

We will print it once before imputation and the second time after imputation of our data to see an aspect of the data imputation

correlationMatrix <- train %>%
  filter(!is.na(Age)) %>%
  select(Survived, Pclass,Age,SibSp,Parch,Fare) %>%
  cor() %>%
  ggcorrplot(lab = T,
             ggtheme =theme_ipsum_rc(grid = F),
             title="Correlation Matrix",hc.order=T,
             colors =rev(viridis(3,alpha=0.7)),
             digits = 1)

correlationMatrix

Imputation

#---------------MICE--------------
set.seed(129)
mice_mod <- mice(train[,!names(train) %in% c('PassengerId','Name','Ticket','Cabin','Survived')],method = 'rf')

 iter imp variable
  1   1  Age
  1   2  Age
  1   3  Age
  1   4  Age
  1   5  Age
  2   1  Age
  2   2  Age
  2   3  Age
  2   4  Age
  2   5  Age
  3   1  Age
  3   2  Age
  3   3  Age
  3   4  Age
  3   5  Age
  4   1  Age
  4   2  Age
  4   3  Age
  4   4  Age
  4   5  Age
  5   1  Age
  5   2  Age
  5   3  Age
  5   4  Age
  5   5  Age
Warning: Number of logged events: 2
mice_output <- complete(mice_mod)

gdistrOriginalData <- train %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
  ggtitle("Distribution of original data") +
  theme_ipsum_rc()
gdistrMICEData <- mice_output %>%
  select(Age) %>%
  ggplot(aes(Age, y = ..density..)) +
  geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) + 
  ggtitle("Distribution of mice output") +
  theme_ipsum_rc()

gridExtra::grid.arrange(gdistrOriginalData,gdistrMICEData,nrow = 1)


train$Age <- mice_output$Age

missing_values <- train %>% summarize_all(funs(sum(is.na(.))/n()))

missing_values <- gather(missing_values, key="feature", value="missing_pct")

missing_values

missing_values %>% 
  ggplot(aes(x=reorder(feature,-missing_pct),y=missing_pct)) +
  geom_bar(stat="identity",fill="red") +
  coord_flip() + # to flip the graph
  xlab("Features") +
  ylab("Percentage of missing values")+
  scale_y_continuous(labels=scales::percent) +
  theme_ipsum()

Correlation matrix after imputation

correlationMatrix <- train %>%
  filter(!is.na(Age)) %>%
  select(Survived, Pclass,Age,SibSp,Parch,Fare) %>%
  cor() %>%
  ggcorrplot(lab = T,
             ggtheme =theme_ipsum_rc(grid = F),
             title="Correlation Matrix",hc.order=T,
             colors =rev(viridis(3,alpha=0.7)),
             digits = 1)

correlationMatrix

Population mean and standard deviation

train %>%
  summarise(Age_mean = mean(Age,na.rm = T), Age_sd = sd(Age,na.rm = T))

Determining the distrubutuion of Age and Fare By inspection

Though determination of the distribution using inspection is likely not going to be effective we are going to the KS-test in a later section

Age

The histogram of the Age feature look very much like a normal distribution, yet it’s not a normal distribution itself.

Fare

The histogram of the Fare feature fits the \(\chi^2\) distribution.

Plotting The Data

We include even more plots of our data to get a better understanding of it, help us see hidden correlations and ways to facilitate our analysis

Class of Passenger Vs Survived

gPclassSurvived <- train %>%
  select(Pclass,Survived) %>%
  ggplot(aes(as_factor(Pclass),fill=as_factor(Survived))) + 
  geom_bar(position = "fill") +
  scale_y_continuous(labels=scales::percent) +
  theme_ipsum_rc() + 
  labs(x = "Classes",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived"))

gPclassSurvived

From this Plot, it seems clear that people from the upper classes had higher survival rates, thought this seemed obvious from the beginning.

Siblings and Spouses Vs Survived

gSibSpSurvived <- train %>%
  select(SibSp,Survived) %>%
  ggplot(aes(as_factor(SibSp),fill=as_factor(Survived))) +
  geom_bar(position = "fill") + 
  scale_y_continuous(labels = scales::percent) +
  labs(x = "Siblings and Spouses",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
  theme_ipsum()

gSibSpSurvived

This plot highlight a point that might be counter-intuitive that people that have no siblings or spouses had lower survival rates than those with at least one sibling or a spouse

Number of children/parents Vs Survived

gParchSurvived <- train %>%
  select(Parch,Survived) %>%
  ggplot(aes(as_factor(Parch),fill=as_factor(Survived))) + 
  geom_bar(position = "fill") +
  scale_y_continuous(label = scales::percent)+
  labs(x = "Number of parents/children",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
  theme_ipsum_rc()
gParchSurvived

Gender VS Survived

gSexSurvived <- train %>%
  select(Sex,Survived) %>%
  ggplot(aes(as_factor(Sex),fill = as_factor(Survived))) + 
  geom_bar(position = "fill") +
  scale_y_continuous(label = scales::percent) + 
  labs(x = "Sex",y = "Survival Rate")+
  scale_fill_discrete(name = "Survived", labels = c("Didn't Survive","Survived")) +
  theme_ipsum_rc()
print(gSexSurvived)

Survival Density and Age

gSurvivalAgeDensity <- ggplot(train[(!is.na(train$Survived) & !is.na(train$Age)),],
            aes(x = Age,
                fill = Survived)) +
  geom_density(alpha=0.5,
               aes(fill=as_factor(Survived))) +
  labs(title="Survival density and Age") +
  scale_x_continuous(breaks = scales::pretty_breaks(n = 10)) +
  theme_ipsum()

gSurvivalAgeDensity

Dashboard of the previous graphs

gridExtra::grid.arrange(gPclassSurvived,
                        gSibSpSurvived,
                        gSexSurvived,
                        gParchSurvived,
                        nrow=2)

train %>%
  group_by(Sex) %>%
  summarise(Age_mean = mean(Age,na.rm=TRUE),
            age_sd = sd(Age,na.rm=T),
            surival_mean = mean(Survived,na.rm =T),
            surival_sd = sd(Survived,na.rm = T))
NA

Sampling

Random sample of size of 50 (Age)

sample_50 <- sample_n(train,size = 50,replace = T) %>%
  summarise(mean = mean(Age), sd = sd(Age))
sample_50

Sampling distrubution with a fixed size (50) (mean)

numOfSamples <- seq(50,1000,50)

smplngDstrbtnRpsChng <- tibble()

for(i in numOfSamples){
  
  for(y in 1:i){
    nsample <- sample_n(train,size=50,replace=T) %>%
      select(Age)
    newRow <- nrow(smplngDstrbtnRpsChng) + 1
    smplngDstrbtnRpsChng[newRow,"reps"] <- i
    smplngDstrbtnRpsChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
  }
  
}

gSamplingReps <- smplngDstrbtnRpsChng %>%
  plot_ly(
    x = ~x_bar,
    frame = ~reps,
    type = "histogram"
  )
gSamplingReps

While no. of sample size increase the variability of sampling distribution decrease and the mean increase.

Sampling distribution with a fixed repetitions and different sizes (mean)

sizes <- seq(20,260,20)
smplngDstrbtnSzChng <- tibble()
for(i in sizes){
  for(y in 1:1500){
    nsample <- sample_n(train,size=i,replace=T) %>%
      select(Age)
    newRow <- nrow(smplngDstrbtnSzChng) + 1
    smplngDstrbtnSzChng[newRow,"sizes"] <- i
    smplngDstrbtnSzChng[newRow,"x_bar"] <- mean(nsample$Age,na.rm = T)
  }
}
gSamplingSize <- smplngDstrbtnSzChng %>%
  plot_ly(
    x = ~x_bar,
    frame = ~sizes,
    type = "histogram"
  )
gSamplingSize

While no. of sample size increase the variability of sampling distribution decrease,and The sample distribution mean will be normally distributed as long as the sample size is more than 30.

sampling distribution of variance with different sizes and fixed repitions.

sizes <- c(2,20,25,30,35,40,45,50)
smplngDstrbtnSzChngVariance <- tibble()
for(i in sizes){
  for(y in 1:1500){
    nsample <- sample_n(train,size=i,replace=T) %>%
      select(Age)
    newRow <- nrow(smplngDstrbtnSzChngVariance) + 1
    smplngDstrbtnSzChngVariance[newRow,"sizes"] <- i
    smplngDstrbtnSzChngVariance[newRow,"variance"] <- sd(nsample$Age,na.rm = T)**2
  }
}
gSamplingSizeVariance <- smplngDstrbtnSzChngVariance %>%
  plot_ly(
    x = ~variance,
    frame = ~sizes,
    type = "histogram"
  )
gSamplingSizeVariance

MME and MLE

\[ M_1 = E(x) = \frac{1}{n} \sum^{n}_{i = 1} x_i \] \[ M_2 = E(x^2) = \sigma^2 + (E(x))^2 = \frac{1}{n} \sum^{n}_{i = 1} {x_i}^2 \] \[ \mu = \frac{1}{2} \sum^{n}_{i =1} x_i = M_1 \] \[ \sigma^2 = E(x^2) - (E(x))^2 = \frac{1}{n} \sum^{n}_{i=1} {x_i}^2 - \left( \frac{1}{n} \sum^{n}_{i = 1} x_i \right)^2 \]

Size 50

# Sampling
n_size = 50
nsample <- sample_n(train,size=n_size,replace=T)
# MME
mean_est1=sum(nsample$Age) / n_size
var_est1 = (sum(nsample$Age^2) / n_size) - (mean_est1)^2
mean_est1
var_est1
bias(actual = mean(train$Age,na.rm = T), predicted = mean_est1)
# MLE
NLL = function(pars,data){
  # Extract parameters from the vector
  mu = pars[1]
  sigma = pars[2]
  NLL = -sum(dnorm(x = data , mean = mu, sd = sigma , log = TRUE))
  # Log of pdf
  # negative to get max
}
mle <- optim(par = c(mu= .2 , sigma = 1.5),fn=NLL,data=nsample$Age,control = list(parscale=c(mu= .2 , sigma = 1.5)))
mle$par[1]

bias(actual = mean(train$Age,na.rm = T), predicted = mle$par[1])

Size 200

# Sampling
n_size = 200
nsample <- sample_n(train,size=n_size,replace=T)
# MME
mean_est1=sum(nsample$Age) / n_size
var_est1 = (sum(nsample$Age^2) / n_size) - (mean_est1)^2
mean_est1
var_est1
bias(actual = mean(train$Age,na.rm = T), predicted = mean_est1)
# MLE
NLL = function(pars,data){
  # Extract parameters from the vector
  mu = pars[1]
  sigma = pars[2]
  NLL = -sum(dnorm(x = data , mean = mu, sd = sigma , log = TRUE))
  # Log of pdf
  # negative to get max
}
mle <- optim(par = c(mu= .2 , sigma = 1.5),fn=NLL,data=nsample$Age,control = list(parscale=c(mu= .2 , sigma = 1.5)))
mle$par[1]

bias(actual = mean(train$Age,na.rm = T), predicted = mle$par[1])

Male Age - Female Age

age_male <- train %>%
  select(Age,Sex) %>%
  mutate(Sex = as_factor(Sex)) %>%
  filter(Sex == "male")
age_female <- train %>%
  select(Age,Sex) %>%
  mutate(Sex = as_factor(Sex)) %>%
  filter(Sex == "female")

sample_age_male_50 <- age_male %>%
  rep_sample_n(size = 50,reps = 15000,replace = T) %>%
  summarise(age_male_bar = mean(Age,na.rm = T))
sample_age_female_50 <- age_female %>%
  rep_sample_n(size = 50,reps = 15000,replace = T) %>%
  summarise(age_female_bar = mean(Age,na.rm = T))

samplediff_means <- sample_age_male_50$age_male_bar -  sample_age_female_50$age_female_bar %>%
  as_tibble()

gsamplediff_means <- samplediff_means %>%
  ggplot(aes(value, y = ..density..)) +
  geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) + 
  geom_density(fill=inferno(1,begin = 0.5,alpha = 0.5),color = inferno(1,begin=0)) +
  ggtitle("Distribution") +
  theme_ipsum_rc()
gsamplediff_means

Survived Male - Survived Female

survived_male <- train %>%
  select(Survived,Sex) %>%
  filter(Sex == "male")

survived_female <- train %>%
  select(Survived,Sex) %>%
  filter(Sex == "female")

sample_survive_male_50 <- survived_male %>%
  rep_sample_n(size = 50,reps = 15000,replace = T)
sample_survive_female_50 <- survived_female %>%
  rep_sample_n(size = 50,reps = 15000,replace = T)
samplediff_survived <- sample_survive_male_50$Survived - sample_survive_female_50$Survived %>% 
  as_tibble()

gsamplediff_survived <- samplediff_survived %>%
  ggplot(aes(value)) +
  geom_histogram(bins = 25,binwidth = 1,color=inferno(1,alpha=1)) + 
  ggtitle("Distribution") +
  theme_ipsum_rc()
gsamplediff_survived

The plot is a bit hard to understand so it needs a bit of explanation. \(-1\) represents the female survived, \(0\) represents the neither of them survived, and \(1\) means that the male survived

After inspecting the plot, it becomes Crystal clear that there was a bias in the rescue process where rescuers preferred to save female more than males.

Confidence Intervals

n_size = 10
nsample <- sample_n(train,size= n_size,replace=T)

# Calculate The Mean
mean_est1=mean(nsample$Age,na.rm = T)

# Calculate The Standard Deviation
sd_est1=sd(nsample$Age,na.rm = T)

# Computing The Error Using the qnorm() Function to Calculate The Normal Distribution 
error=qt(0.975,df = 9, lower.tail = T)*(sd_est1/sqrt(n_size)) # at lower.tail = T probability is P[X<=x] , otherwise P[X>x]

#Determining The Mean Interval[]
left <- mean_est1-error
right <- mean_est1+error

print(paste0("[",left,",",right,"]"))
n_size = 50
nsample <- sample_n(train,size=n_size,replace=T)

# Calculate The Mean
mean_est2=mean(nsample$Age,na.rm = T)

# Calculate The Standard Deviation
sd_est2=sd(nsample$Age,na.rm = T)

# Computing The Error Using the qnorm() Function to Calculate The Normal Distribution 
error=qnorm(0.975)*(sd_est1/sqrt(n_size))

#Determining The Mean Interval[]
left <- mean_est2-error
right <- mean_est2+error

print(paste0("[",left,",",right,"]"))

Multiplication and addition of constant to the sample values

Multiplication

n_size = 200

nsample <- sample_n(train,size = n_size,replace = T)

nsample %>%
  summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)^2)

nsample$Age <- nsample$Age *5
nsample %>%
  summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)^2)

Addition

n_size = 200

nsample <- sample_n(train,size = n_size,replace = T)

nsample %>%
  summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)^2)

nsample$Age <- nsample$Age + 5
nsample %>%
  summarise(Age_mean = mean(Age), Age_sd = sd(Age),Age_var = sd(Age)^2)

kernel distribution

A kernel distribution is a nonparametric representation of the probability density function (\(pdf\)) of a random variable in any population

The kernel smoothing function defines the shape of the curve used to generate the pdf Kernel distribution is Quote from histogram in other word (smooth representation of a histogram) That the integral =1 There is a benefit of smooth representation of a histogram like Ignores irregularities and outliers , more efficient in approximation so it deals better with large data than small data

\[ \hat{f_h} = \frac{1}{n} = \sum^n_{i = 1} K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]

Rules

Non-weighted Data

\[ \hat{f_h} = \frac{1}{n} \sum^n_{i = 1} K(x-x_i) = \frac{1}{nh} K\left(\frac{x-x_i}{h}\right) \]

Weighted Data

\[ \hat{f_h} = \frac{1}{h} \sum^N_{i=1} w_i K \left(\frac{x-x_i}{h}\right), \qquad \text{where} \sum^N_{i= 1} w_i = 1 \]

Kernel Function

  1. Box
  2. Triangle
  3. Normal
  4. Pantechnicon

Each density curve uses the same input data, but applies a different kernel smoothing function to generate the pdf. The density estimates are roughly comparable, but the shape of each curve varies slightly. For example, the box kernel produces a density curve that is less smooth than the others.

The choice of bandwidth value controls the smoothness of the resulting probability density curve (higher value of \(h\) more smoothing)

Specifying a smaller bandwidth produces a very rough curve, but reveals that there might be two major peaks in the data. Specifying a larger bandwidth produces a curve nearly identical to the kernel function Choosing the optimal (\(h\)) bandwidth methods :

  1. Silverman’s rule of thump that computes an optimal h by assuming that data is normally distributed
  2. Improved Sheather Jones (ISJ) an algorithm is more robust with multimodality data or a lot of data (one disadvantage is it needs to large data )

Bounded domains data: have a constrains like data couldn’t be negative ( -ve lead to probability = 0)

Mirror method

  1. Mirror the data
  2. Sum the original and mirrored kernel density estimate
  3. Chop it so that zero at the boundary side

2D

\(h\) : could be matrix (different \(h\) in different directions)

The choice of norm comes into \(d \ge 2\)

The p-norm is \(||x||_p := (\sum_{i=1} |x|^p)^{\frac{1}{p}}\) - norm-p =1 Manhattan distance - norm-p =2 Euclidean norm - norm-p =inf maximum norm (it’s not obvious in every case which norm is the correct one)

standard euclidean distance is good choice because it invariant under rotation as large data choice of k and p isn’t important so

Kolmogorov-Smirnov Test

Non parametric test

Kolmogorov-Smirnov Test is used to: 1. Decide if a sample comes from a population with an expected continuous distribution (mostly normal distribution) 2. To test for the difference in the shape of two sample distributions

Compare overall shape of distribution, not specifically parameter

Kolmogorov-Smirnov Test is defined by a hypothesis: \(H_0\) : the data follow specific distribution \(F(x) = F_T (x)\) \(H_a\) : the data don’t follow specific distribution \(F(x) \neq F_T (x)\)

KS-test is made between some theoretical cumulative distribution function (\(F_T(x)\)), and a sample cumulative distribution function (\(F_s(x)\)) that measured by the statistic D, which is the greatest vertical distance between them. \[ D = \underbrace{\text{sup}}_{x} | F_s(x) - F_t (x)|\]

  1. Determine \(x\) values
  2. Detemrine frequency of each observation
  3. Calculate cumulative frequency
  4. Calculate \(F_s(x) \rightarrow \frac{\text{cumulative frequence}}{N}\) when \(n > 30\) (\(z\)-scores), when \(n<30\) (\(t\)-scores)
  5. calculate \(F_t(x)\)
  6. Calculate \(D \rightarrow F_s(x) - F_T(x)\)
  7. Choose maximum \(D\)
  8. Calculate \(P\)-value
  9. Determine Kolmogorov’s quartile
  10. Compare \(p\)-value with Kolmogorove’s quartile
LS0tDQp0aXRsZTogIlRpdGFuaWMgRGF0YSBBbmFseXNpcyINCmF1dGhvcjoNCi0gQWhtZWQgQXNocmFmDQotIE9tYXIgR2FtYWwgQWJkZWxheml6DQotIEFobWVkIFlvdXNyaQ0KLSBBaG1lZCBEYXdvb2QNCi0gQWxpYSBNZWRoYXQNCm91dHB1dDoNCiAgaHRtbF9kb2N1bWVudDoNCiAgICB0b2M6IHllcw0KICAgIGRmX3ByaW50OiBwYWdlZA0KICBwZGZfZG9jdW1lbnQ6DQogICAgdG9jOiB5ZXMNCiAgICBudW1iZXJfc2VjdGlvbnM6IHllcw0KICAgIGluY2x1ZGVzOg0KICAgICAgaW5faGVhZGVyOiBwcmVhbWJsZS50ZXgNCiAgICBoaWdobGlnaHQ6IHB5Z21lbnRzDQogIGh0bWxfbm90ZWJvb2s6DQogICAgdG9jOiB5ZXMNCmFsd2F5c19hbGxvd19odG1sOiB5ZXMNCmVkaXRvcl9vcHRpb25zOg0KICBtYXJrZG93bjoNCiAgICB3cmFwOiA4MA0KLS0tDQoNCiMgSW50cm9kdWN0aW9uDQoNCkluIHRoaXMgcHJvamVjdCB3ZSBhcmUgdHJ5aW5nIGZpcnN0IHRvIGV4cGxvcmUgb3VyIGRhdGEgdG8gZ2V0IGEgYmV0dGVyDQp1bmRlcnN0YW5kaW5nIFRvIGFuc3dlciBvdXIgcXVlc3Rpb25zIHdlIGZpcnN0IG5lZWQgdG8gaGF2ZSBhDQpwcmVsaW1pbmFyeSBsb29rIGF0IG91ciBkYXRhLCBzbyB0aGF0IHdlIGNhbiBnZXQgYSBiZXR0ZXIgYSBpZGVhIHdoYXQgd2UNCmFyZSBkZWFsaW5nIHdpdGgsIGFzIHdlbGwgYXMgdGhlIHBvc3NpYmxlIG1pc3NpbmcgZGF0YSBhbmQgcmVsYXRpb25zaGlwcw0KdGhhdCBleGlzdA0KDQojIyBQcmVsaW1pbmFyeSBMb29rIGF0IHRoZSBkYXRhDQoNCldlIG5lZWQgZmlyc3QgdG8gZGVmaW5lIHRoZSBkYXRhIHdlIGhhdmUuDQoNCnwgVmFyaWFibGUgfCBEZWZpbml0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCBLZXkgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8Oi0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXw6LS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tfA0KfCBzdXJ2aXZhbCB8IFN1cnZpdmFsICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8IDAgPSBObywgMSA9IHllcyAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgcGNsYXNzICAgfCB0aWNrZXQgY2xhc3MgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAxID0gMXN0LCAyID0gMm5kLCAzID0gM3JkICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHNleCAgICAgIHwgc2V4ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBhZ2UgICAgICB8IEFnZSBpbiB5ZWFyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgc2lic3AgICAgfCBOdW1iZXIgb2Ygc2libGluZ3Mvc3BvdXNlcyBhYm9hcmQgdGhlIHRpdGFuaWMgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IHBhcmNoICAgIHwgTnVtYmVyIG9mIHBhcmVudHMvY2hpbGRyZW4gYWJvYXJkIHRoZSBUaXRhbmljIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCB0aWNrZXQgICB8IHRpY2tldCBudW1iZXIodW5pcXVlKSAgICAgICAgICAgICAgICAgICAgICAgICB8ICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwNCnwgZmFyZSAgICAgfCBQYXNzZW5nZXIgZmFyZSAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfCAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB8DQp8IGNhYmluICAgIHwgQ2FiaW4gbnVtYmVyICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHwgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgfA0KfCBlbWJhcmtlZCB8IHBvcnQgb2YgZW1iYXJrYXRpb24gICAgICAgICAgICAgICAgICAgICAgICAgICB8IEMgPSBDaGVyYm91cmcsIFEgPSBRdWVlbnMtdG93biwgUyA9IFNvdXRoYW1wdG9uIHwNCg0KIyMgTG9hZGluZyB0aGUgcGFja2FnZXMgYW5kIHRoZSBEYXRhDQoNCmBgYHtyIExvYWRpbmcgUGFja2FnZXMsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgcmVzdWx0cz0naGlkZSd9DQojIExvYWRpbmcgUGFja2FnZXMNCiMjIHRpZHl2ZXJzZSBsb2FkcyBkcGx5ciBhbmQgcmVhZHINCmxpYnJhcnkodGlkeXZlcnNlKQ0KDQojIyBUbyBoYXZlIGRpZmZlcmVudCBjb2xvciBtYXBzDQpsaWJyYXJ5KHZpcmlkaXMpDQoNCiMjIGdncGxvdDIgdG8gcHJvZHVjZSBkaWZmZXJlbnQgcGxvdHMNCmxpYnJhcnkoZ2dwbG90MikNCg0KIyMgdXNlcyBnZ3Bsb3QyIHRvIHByb2R1Y2UgYSBjb3JyZWxhdGlvbiBtYXRyaXggLS0gdGhlIGRhdGEgbXVzdCBiZSBpbiB0aGUgY29ycmVjdCBmb3JtDQpsaWJyYXJ5KGdnY29ycnBsb3QpDQoNCiMjIEdpdmVzIHVzIGJldHRlciB0aGVtZXMNCmxpYnJhcnkoaHJicnRoZW1lcykNCg0KIyMgdG8gdXNlIHNrZXduZXNzIGZ1bi4gdG8gY2FsY3VsYXRlIHNrZXduZXNzIG9mIHRoZSBkaXN0cmlidXRpb24NCmxpYnJhcnkoZTEwNzEpDQoNCiMjIE11bHRpdmFyaWF0ZSBpbXB1dGF0aW9uIHVzaW5nIGNoYWluZWQgZXF1YXRpb25zIC0tIHRvIGltcHV0ZSB0aGUgbWlzc2luZyB2YWx1ZXMgaW4gb3VyIGRhdGENCmxpYnJhcnkobWljZSkNCg0KIyMgTG9hZHMgZGlmZmVyZW50IHN0YXRpc3RpY2FsIGZ1bmN0aW9ucw0KbGlicmFyeShzdGF0c3IpDQoNCiMjIFRvIHByb2R1Y2UgaW50ZXJhY3RpdmUgcGxvdA0KbGlicmFyeShwbG90bHkpDQoNCiMjIFRvIHVzZSBiaWFzIGZ1bmN0aW9uDQpsaWJyYXJ5KE1ldHJpY3MpDQoNCg0KIyBMb2FkaW5nIFRyYWluaW5nIERhdGENCnRyYWluIDwtIHJlYWRfY3N2KCJkYXRhL3RyYWluLmNzdiIpDQoNCiMgTG9hZGluZyBUZXN0aW5nIERhdGENCnRlc3QgPC0gcmVhZF9jc3YoImRhdGEvdGVzdC5jc3YiKQ0KDQojIEJpbmRpbmcgdGhlbSBpbnRvIGEgZnVsbCBkYXRhIGZyYW1lDQpkZiA8LSBiaW5kX3Jvd3ModHJhaW4sdGVzdCkNCmBgYA0KDQojIEV4cGxvcmF0aW9uIE9mIFRoZSBEYXRhDQoNCiMjIFN1bW1hcnkgb2YgRGF0YQ0KDQpgYGB7ciBEYXRhIFN1bW1hcnl9DQpzdW1tYXJ5KHRyYWluKQ0KYGBgDQoNCiMjIENhdGVnb3JpZXMgb2YgRmVhdHVyZXMNCg0KPiBRdWFudGl0YXRpdmUgZGF0YSBhcmUgbWVhc3VyZXMgb2YgdmFsdWVzIG9yIGNvdW50cyBhbmQgYXJlIGV4cHJlc3NlZA0KPiBhcyBudW1iZXJzLg0KDQo+IFF1YWxpdGF0aXZlIGRhdGEgYXJlIG1lYXN1cmVzIG9mICd0eXBlcycgYW5kIG1heSBiZSByZXByZXNlbnRlZCBieSBhDQo+IG5hbWUsIHN5bWJvbCwgb3IgYSBudW1iZXIgY29kZS4NCg0KIyMjIFF1YWxpdGl2ZQ0KDQpDYXRlZ29yaWNhbDogU3Vydml2ZWQsIFNleCwgYW5kIEVtYmFya2VkLiBPcmRpbmFsOiBQY2xhc3MuIE5vbWluYWw6DQpOYW1lLg0KDQojIyMgUXVhbnRpdGl2ZQ0KDQpDb250aW51b3VzOiBBZ2UsIEZhcmUuIERpc2NyZXRlOiBTaWJTcCwgUGFyY2guDQoNCiMjIyBNaXggdHlwZXMNCg0KVGlja2V0IGlzIGEgbWl4IG9mIG51bWVyaWMgYW5kIGFscGhhbnVtZXJpYyBkYXRhIHR5cGVzIENhYmluIGlzIG1peA0KYmV0d2VlbiBhbHBoYSBhbmQgbnVtZXJpYw0KDQojIyBFeHBsb3JpbmcgTWlzc2luZyBEYXRhLg0KDQpDaGVja2luZyBmb3IgTWlzc2luZyB2YWx1ZXMgaW4gZWFjaCBmZWF0dXJlDQoNCmBgYHtyIE1pc3NpbmcgVmFsdWVzfQ0KY29sU3Vtcyhpcy5uYSh0cmFpbikpDQpgYGANCg0KYGBge3IgTWlzc2luZyB2YWx1ZXMgcGxvdH0NCm1pc3NpbmdfdmFsdWVzIDwtIHRyYWluICU+JSBzdW1tYXJpemVfYWxsKGZ1bnMoc3VtKGlzLm5hKC4pKS9uKCkpKQ0KDQptaXNzaW5nX3ZhbHVlcyA8LSBnYXRoZXIobWlzc2luZ192YWx1ZXMsIGtleT0iZmVhdHVyZSIsIHZhbHVlPSJtaXNzaW5nX3BjdCIpDQoNCm1pc3NpbmdfdmFsdWVzDQoNCm1pc3NpbmdfdmFsdWVzICU+JSANCiAgZ2dwbG90KGFlcyh4PXJlb3JkZXIoZmVhdHVyZSwtbWlzc2luZ19wY3QpLHk9bWlzc2luZ19wY3QpKSArDQogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5IixmaWxsPSJyZWQiKSArDQogIGNvb3JkX2ZsaXAoKSArICMgdG8gZmxpcCB0aGUgZ3JhcGgNCiAgeGxhYigiRmVhdHVyZXMiKSArDQogIHlsYWIoIlBlcmNlbnRhZ2Ugb2YgbWlzc2luZyB2YWx1ZXMiKSsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscz1zY2FsZXM6OnBlcmNlbnQpICsNCiAgdGhlbWVfaXBzdW0oKQ0KYGBgDQoNCiMjIyBNaXNzaW5nIGRhdGEsIGl0IGlzIG5vcm1hbD8NCg0KSXQgaXMgcXVpdGUgbm9ybWFsIHRvIHNlZSBtaXNzaW5nIGRhdGEgaW4gYW55IGRhdGEtc2V0IGFzIHN1Y2ggZGF0YSBpcw0KY29sbGVjdGVkIGJ5IG1hbnVhbGx5IHdoaWNoIG1lYW5zIHRoYXQgdGhlcmUgbWlnaHQgYmUgc29tZSBlcnJvci4NCk1pc3NpbmcgZGF0YSBwcmVzZW50IHZhcmlvdXMgcHJvYmxlbXMuIEZpcnN0LCB0aGUgYWJzZW5jZSBvZiBkYXRhDQpyZWR1Y2VzIHN0YXRpc3RpY2FsIHBvd2VyLCB3aGljaCByZWZlcnMgdG8gdGhlIHByb2JhYmlsaXR5IHRoYXQgdGhlIHRlc3QNCndpbGwgcmVqZWN0IHRoZSBudWxsIGh5cG90aGVzaXMgd2hlbiBpdCBpcyBmYWxzZS4gU2Vjb25kLCB0aGUgbG9zdCBkYXRhDQpjYW4gY2F1c2UgYmlhcyBpbiB0aGUgZXN0aW1hdGlvbiBvZiBwYXJhbWV0ZXJzLiBUaGlyZCwgaXQgY2FuIHJlZHVjZSB0aGUNCnJlcHJlc2VudGF0aXZlbmVzcyBvZiB0aGUgc2FtcGxlcy4NCg0KIyMjIFRoZSBzb2x1dGlvbiB0byB0aGUgbWlzc2luZyBkYXRhDQoNCiMjIyMgUmVtb3ZhbA0KDQpUaGUgcmVtb3ZhbCBvZiB0aGUgb2JzZXJ2YXRpb25zIHRoYXQgY29udGFpbiBtaXNzaW5nIG1pZ2h0IGNhdXNlIGENCmJpZ2dlciBpc3N1ZSB3aGVyZSBpdCBtaWdodCBhbiBldmVuIGJpZ2dlciBsb3NzIG9mIGluZm9ybWF0aW9uIHdoaWNoDQpjYXVzZSBiaWFzIGluIG91ciBlc3RpbWF0aW9uLiBCdXQgb24gY2xvc2Ugb2JzZXJ2YXRpb24gb2Ygb3VyIGRhdGEgd2UNCmNhbiBpZGVudGlmeSB0aGF0IHRoZXJlIGFyZSBzb21lIGZlYXR1cmVzIHRoYXQgYXJlIG5vdCBleHRyZW1lbHkgdXNlZnVsDQphbmQgY29udGFpbiBtaXNzaW5nIGRhdGEsIHNvIHdlIGNhbiBkcm9wIHN1Y2ggZmVhdHVyZXMuDQoNCmBgYHtyIGRyb3BpbmcgY2FiaW59DQp0cmFpbiA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KCFDYWJpbikNCnN1bW1hcnkodHJhaW4pDQpgYGANCg0KIyMjIyBJbXB1dGF0aW9uDQoNCkltcHV0aW5nIHRoZSBtaXNzaW5nIGRhdGEgZ2l2ZXMgdGhlIGFkdmFudGFnZSB0aGF0IHdlIHVzZSBhIGxlYXJuaW5nDQptb2RlbCB0byBwcmVkaWN0IHN1Y2ggbWlzc2luZyBkYXRhIGFuZCBtYWludGFpbiB0aGUgZGlzdHJpYnV0aW9uIG9mIG91cg0KZGF0YS4NCg0KIyMgSGlzdG9ncmFtIG9mIEFnZSBmZWF0dXJlLg0KDQpgYGB7ciBBZ2UgaGlzdG9ncmFtfQ0KZ0FnZURlbnNpdHkgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UpICU+JQ0KICBnZ3Bsb3QoYWVzKEFnZSwgeSA9IC4uZGVuc2l0eS4uKSkgKw0KICBnZW9tX2hpc3RvZ3JhbShiaW5zID0gMjAsYmlud2lkdGggPSAxLGNvbG9yPWluZmVybm8oMSxhbHBoYT0xKSkgKyANCiAgZ2VvbV9kZW5zaXR5KGZpbGw9aW5mZXJubygxLGJlZ2luID0gMC41LGFscGhhID0gMC41KSxjb2xvciA9IGluZmVybm8oMSxiZWdpbj0wKSkgKyANCiAgYW5ub3RhdGUoDQogICAgInRleHQiLA0KICAgIHggPSA3MCwNCiAgICB5ID0gMC4wNCwNCiAgICBsYWJlbCA9IHBhc3RlKCJTa2V3bmVzczoiLHNrZXduZXNzKHRyYWluJEFnZSxuYS5ybSA9IFQpKSwNCiAgICBjb2xvdXIgPSBpbmZlcm5vKDEsYmVnaW4gPSAwLjEpLA0KICAgIHNpemUgPSA0DQogICkgKyANCiAgdGhlbWVfaXBzdW1fcmMoKQ0KDQpnQWdlRGVuc2l0eQ0KYGBgDQoNCiMjIyBQb3B1bGF0aW9uIG1lYW4gYW5kIHN0YW5kYXJkIGRldmlhdGlvbiBvZiBBZ2UgZmVhdHVyZSBCZWZvcmUgaW1wdXRhdGlvbg0KDQpgYGB7ciBTdW1tYXJ5IG9mIEFnZSBmZWF0dXJlfQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlLG5hLnJtID0gVCksIEFnZV9zZCA9IHNkKEFnZSxuYS5ybSA9IFQpKQ0KYGBgDQoNCiMjIEhpc3RvZ3JhbSBvZiBGYXJlIGZlYXR1cmUNCg0KYGBge3IgSGlzdG9ncmFtIG9mIEZhcmUgRmVhdHVyZX0NCmdGYXJlRGVuc2l0eSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEZhcmUpICU+JQ0KICBnZ3Bsb3QoYWVzKEZhcmUsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDIwLGJpbndpZHRoID0gMSxjb2xvcj12aXJpZGlzKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSB2aXJpZGlzKDEsYmVnaW49MCkpICsgDQogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsMC4wNSkpKw0KICB0aGVtZV9pcHN1bV9yYygpICsgDQogIGFubm90YXRlKA0KICAgICJ0ZXh0IiwNCiAgICB4ID0gMjAwLA0KICAgIHkgPSAwLjA1LA0KICAgIGxhYmVsID0gcGFzdGUoIlNrZXduZXNzIixza2V3bmVzcyh0cmFpbiRGYXJlKSksDQogICAgY29sb3VyID0gImJsYWNrIiwNCiAgICBzaXplID0gNA0KICApDQoNCmdGYXJlRGVuc2l0eSANCmBgYA0KDQojIyMgUG9wdWxhdGlvbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYWl0b24gb2YgRmFyZSBmZWF0dXJlLg0KDQpgYGB7ciBTdW1tYXJ5IG9mIEZhcmUgRmVhdHVyZX0NCnRyYWluICU+JQ0KICBzdW1tYXJpc2UoRmFyZV9tZWFuID0gbWVhbihGYXJlLG5hLnJtID0gVCksIEZhcmVfc2QgPSBzZChGYXJlLG5hLnJtID0gVCkpDQpgYGANCg0KIyMgSW1wdXRpbmcgdGhlIG1pc3NpbmcgYWdlIGZlYXR1cmUNCg0KIyMjIENvcnJlbGF0aW9uIG1hdHJpeCBiZWZvcmUgaW1wdXRhdGlvbg0KDQpXZSBhcmUgZ29pbmcgdG8gdXNlIGNvcnJlbGF0aW9uIG1hdHJpeCBvZiB0aGUgbnVtZXJpY2FsIGRhdGEgdG8gYXNzZXNzDQp0aGUgY29ycmVsYXRpb24sIHdoaWNoIG1pZ2h0IGdpdmVzIGEgYmV0dGVyIGlkZWEgb2Ygd2hpY2ggZmVhdHVyZSBtaWdodA0KYmUgaW1wb3J0YW50DQoNClRoZSBmYXJlIGZlYXR1cmVzIHNlZW1zIHRvIGJlIHRoZSBtb3N0IGNvcnJlbGF0ZWQgZmVhdHVyZSB0byBzdXJ2aXZhbCBvZg0KdGhlIHBhc3NlbmdlcnMsIGJ1dCBpdCBkb2Vzbid0IG5lZ2F0ZSB0aGUgaW1wb3J0YW5jZSBvZiB0aGUgb3RoZXINCmZlYXR1cmVzIGluIHRoZSBkYXRhLiBXaGljaCBtZWFucyB0aGF0IHdlIHdpbGwgc3RhcnQgYnkgY29tcGFyaW5nIHRoZQ0KZWFjaCB0aGF0IHdlIGNvbnNpZGVyIHRvIGJlIGltcG9ydGFudCBhZ2FpbnN0IHN1cnZpdmFsIGZlYXR1cmUNCg0KV2Ugd2lsbCBwcmludCBpdCBvbmNlIGJlZm9yZSBpbXB1dGF0aW9uIGFuZCB0aGUgc2Vjb25kIHRpbWUgYWZ0ZXIgaW1wdXRhdGlvbiBvZiBvdXIgZGF0YSB0byBzZWUgYW4gYXNwZWN0IG9mIHRoZSBkYXRhIGltcHV0YXRpb24NCg0KYGBge3IgQ29ycmVsYXRpb24gTWF0cml4IDEsIGZpZy5hbGlnbj0nY2VudGVyJ30NCmNvcnJlbGF0aW9uTWF0cml4IDwtIHRyYWluICU+JQ0KICBmaWx0ZXIoIWlzLm5hKEFnZSkpICU+JQ0KICBzZWxlY3QoU3Vydml2ZWQsIFBjbGFzcyxBZ2UsU2liU3AsUGFyY2gsRmFyZSkgJT4lDQogIGNvcigpICU+JQ0KICBnZ2NvcnJwbG90KGxhYiA9IFQsDQogICAgICAgICAgICAgZ2d0aGVtZSA9dGhlbWVfaXBzdW1fcmMoZ3JpZCA9IEYpLA0KICAgICAgICAgICAgIHRpdGxlPSJDb3JyZWxhdGlvbiBNYXRyaXgiLGhjLm9yZGVyPVQsDQogICAgICAgICAgICAgY29sb3JzID1yZXYodmlyaWRpcygzLGFscGhhPTAuNykpLA0KICAgICAgICAgICAgIGRpZ2l0cyA9IDEpDQoNCmNvcnJlbGF0aW9uTWF0cml4DQpgYGANCg0KIyMjIEltcHV0YXRpb24NCg0KYGBge3IgSW1wdXRhdGlvbn0NCiMtLS0tLS0tLS0tLS0tLS1NSUNFLS0tLS0tLS0tLS0tLS0NCnNldC5zZWVkKDEyOSkNCm1pY2VfbW9kIDwtIG1pY2UodHJhaW5bLCFuYW1lcyh0cmFpbikgJWluJSBjKCdQYXNzZW5nZXJJZCcsJ05hbWUnLCdUaWNrZXQnLCdDYWJpbicsJ1N1cnZpdmVkJyldLG1ldGhvZCA9ICdyZicpDQptaWNlX291dHB1dCA8LSBjb21wbGV0ZShtaWNlX21vZCkNCg0KZ2Rpc3RyT3JpZ2luYWxEYXRhIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoQWdlKSAlPiUNCiAgZ2dwbG90KGFlcyhBZ2UsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1LGJpbndpZHRoID0gMSxjb2xvcj1pbmZlcm5vKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSBpbmZlcm5vKDEsYmVnaW49MCkpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIG9mIG9yaWdpbmFsIGRhdGEiKSArDQogIHRoZW1lX2lwc3VtX3JjKCkNCmdkaXN0ck1JQ0VEYXRhIDwtIG1pY2Vfb3V0cHV0ICU+JQ0KICBzZWxlY3QoQWdlKSAlPiUNCiAgZ2dwbG90KGFlcyhBZ2UsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1LGJpbndpZHRoID0gMSxjb2xvcj1pbmZlcm5vKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSBpbmZlcm5vKDEsYmVnaW49MCkpICsgDQogIGdndGl0bGUoIkRpc3RyaWJ1dGlvbiBvZiBtaWNlIG91dHB1dCIpICsNCiAgdGhlbWVfaXBzdW1fcmMoKQ0KDQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShnZGlzdHJPcmlnaW5hbERhdGEsZ2Rpc3RyTUlDRURhdGEsbnJvdyA9IDEpDQoNCnRyYWluJEFnZSA8LSBtaWNlX291dHB1dCRBZ2UNCg0KbWlzc2luZ192YWx1ZXMgPC0gdHJhaW4gJT4lIHN1bW1hcml6ZV9hbGwoZnVucyhzdW0oaXMubmEoLikpL24oKSkpDQoNCm1pc3NpbmdfdmFsdWVzIDwtIGdhdGhlcihtaXNzaW5nX3ZhbHVlcywga2V5PSJmZWF0dXJlIiwgdmFsdWU9Im1pc3NpbmdfcGN0IikNCg0KbWlzc2luZ192YWx1ZXMNCg0KbWlzc2luZ192YWx1ZXMgJT4lIA0KICBnZ3Bsb3QoYWVzKHg9cmVvcmRlcihmZWF0dXJlLC1taXNzaW5nX3BjdCkseT1taXNzaW5nX3BjdCkpICsNCiAgZ2VvbV9iYXIoc3RhdD0iaWRlbnRpdHkiLGZpbGw9InJlZCIpICsNCiAgY29vcmRfZmxpcCgpICsgIyB0byBmbGlwIHRoZSBncmFwaA0KICB4bGFiKCJGZWF0dXJlcyIpICsNCiAgeWxhYigiUGVyY2VudGFnZSBvZiBtaXNzaW5nIHZhbHVlcyIpKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bSgpDQpgYGANCiMjIyBDb3JyZWxhdGlvbiBtYXRyaXggYWZ0ZXIgaW1wdXRhdGlvbg0KDQpgYGB7ciBDb3JyZWxhdGlvbiBNYXRyaXggMiwgZmlnLmFsaWduPSdjZW50ZXInfQ0KY29ycmVsYXRpb25NYXRyaXggPC0gdHJhaW4gJT4lDQogIGZpbHRlcighaXMubmEoQWdlKSkgJT4lDQogIHNlbGVjdChTdXJ2aXZlZCwgUGNsYXNzLEFnZSxTaWJTcCxQYXJjaCxGYXJlKSAlPiUNCiAgY29yKCkgJT4lDQogIGdnY29ycnBsb3QobGFiID0gVCwNCiAgICAgICAgICAgICBnZ3RoZW1lID10aGVtZV9pcHN1bV9yYyhncmlkID0gRiksDQogICAgICAgICAgICAgdGl0bGU9IkNvcnJlbGF0aW9uIE1hdHJpeCIsaGMub3JkZXI9VCwNCiAgICAgICAgICAgICBjb2xvcnMgPXJldih2aXJpZGlzKDMsYWxwaGE9MC43KSksDQogICAgICAgICAgICAgZGlnaXRzID0gMSkNCg0KY29ycmVsYXRpb25NYXRyaXgNCmBgYA0KDQojIyMgUG9wdWxhdGlvbiBtZWFuIGFuZCBzdGFuZGFyZCBkZXZpYXRpb24NCg0KYGBge3IgU3VtbWFyeSBvZiBBZ2UgZmVhdHVyZSBBZnRlciBJbXB1dGF0aW9ufQ0KdHJhaW4gJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlLG5hLnJtID0gVCksIEFnZV9zZCA9IHNkKEFnZSxuYS5ybSA9IFQpKQ0KYGBgDQoNCiMjIERldGVybWluaW5nIHRoZSBkaXN0cnVidXR1aW9uIG9mIEFnZSBhbmQgRmFyZSBCeSBpbnNwZWN0aW9uDQoNClRob3VnaCBkZXRlcm1pbmF0aW9uIG9mIHRoZSBkaXN0cmlidXRpb24gdXNpbmcgaW5zcGVjdGlvbiBpcyBsaWtlbHkgbm90DQpnb2luZyB0byBiZSBlZmZlY3RpdmUgd2UgYXJlIGdvaW5nIHRvIHRoZSBLUy10ZXN0IGluIGEgbGF0ZXIgc2VjdGlvbg0KDQojIyMgQWdlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEFnZSBmZWF0dXJlIGxvb2sgdmVyeSBtdWNoIGxpa2UgYSBub3JtYWwNCmRpc3RyaWJ1dGlvbiwgeWV0IGl0J3Mgbm90IGEgbm9ybWFsIGRpc3RyaWJ1dGlvbiBpdHNlbGYuDQoNCiMjIyBGYXJlDQoNClRoZSBoaXN0b2dyYW0gb2YgdGhlIEZhcmUgZmVhdHVyZSBmaXRzIHRoZSAkXGNoaV4yJCBkaXN0cmlidXRpb24uDQoNCiMjIFBsb3R0aW5nIFRoZSBEYXRhDQoNCldlIGluY2x1ZGUgZXZlbiBtb3JlIHBsb3RzIG9mIG91ciBkYXRhIHRvIGdldCBhIGJldHRlciB1bmRlcnN0YW5kaW5nIG9mDQppdCwgaGVscCB1cyBzZWUgaGlkZGVuIGNvcnJlbGF0aW9ucyBhbmQgd2F5cyB0byBmYWNpbGl0YXRlIG91ciBhbmFseXNpcw0KDQojIyMgQ2xhc3Mgb2YgUGFzc2VuZ2VyIFZzIFN1cnZpdmVkDQoNCmBgYHtyIFBjbGFzc1ZTU3Vydml2ZWQsZmlnLmFsaWduPSdjZW50ZXInfQ0KZ1BjbGFzc1N1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoUGNsYXNzLFN1cnZpdmVkKSAlPiUNCiAgZ2dwbG90KGFlcyhhc19mYWN0b3IoUGNsYXNzKSxmaWxsPWFzX2ZhY3RvcihTdXJ2aXZlZCkpKSArIA0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKw0KICBzY2FsZV95X2NvbnRpbnVvdXMobGFiZWxzPXNjYWxlczo6cGVyY2VudCkgKw0KICB0aGVtZV9pcHN1bV9yYygpICsgDQogIGxhYnMoeCA9ICJDbGFzc2VzIix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKQ0KDQpnUGNsYXNzU3Vydml2ZWQNCmBgYA0KDQpGcm9tIHRoaXMgUGxvdCwgaXQgc2VlbXMgY2xlYXIgdGhhdCBwZW9wbGUgZnJvbSB0aGUgdXBwZXIgY2xhc3NlcyBoYWQgaGlnaGVyIHN1cnZpdmFsIHJhdGVzLCB0aG91Z2h0IHRoaXMgc2VlbWVkIG9idmlvdXMgZnJvbSB0aGUgYmVnaW5uaW5nLg0KDQojIyMgU2libGluZ3MgYW5kIFNwb3VzZXMgVnMgU3Vydml2ZWQNCg0KYGBge3IgU2liU3BWU1N1cnZpdmVkLGZpZy5hbGlnbj0nY2VudGVyJ30NCmdTaWJTcFN1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoU2liU3AsU3Vydml2ZWQpICU+JQ0KICBnZ3Bsb3QoYWVzKGFzX2ZhY3RvcihTaWJTcCksZmlsbD1hc19mYWN0b3IoU3Vydml2ZWQpKSkgKw0KICBnZW9tX2Jhcihwb3NpdGlvbiA9ICJmaWxsIikgKyANCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVscyA9IHNjYWxlczo6cGVyY2VudCkgKw0KICBsYWJzKHggPSAiU2libGluZ3MgYW5kIFNwb3VzZXMiLHkgPSAiU3Vydml2YWwgUmF0ZSIpKw0KICBzY2FsZV9maWxsX2Rpc2NyZXRlKG5hbWUgPSAiU3Vydml2ZWQiLCBsYWJlbHMgPSBjKCJEaWRuJ3QgU3Vydml2ZSIsIlN1cnZpdmVkIikpICsNCiAgdGhlbWVfaXBzdW0oKQ0KDQpnU2liU3BTdXJ2aXZlZA0KYGBgDQoNClRoaXMgcGxvdCBoaWdobGlnaHQgYSBwb2ludCB0aGF0IG1pZ2h0IGJlIGNvdW50ZXItaW50dWl0aXZlIHRoYXQgcGVvcGxlIHRoYXQgaGF2ZSBubyBzaWJsaW5ncyBvciBzcG91c2VzIGhhZCBsb3dlciBzdXJ2aXZhbCByYXRlcyB0aGFuIHRob3NlIHdpdGggYXQgbGVhc3Qgb25lIHNpYmxpbmcgb3IgYSBzcG91c2UNCg0KIyMjIE51bWJlciBvZiBjaGlsZHJlbi9wYXJlbnRzIFZzIFN1cnZpdmVkDQoNCmBgYHtyIGZpZy5hbGlnbj0nY2VudGVyJ30NCmdQYXJjaFN1cnZpdmVkIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoUGFyY2gsU3Vydml2ZWQpICU+JQ0KICBnZ3Bsb3QoYWVzKGFzX2ZhY3RvcihQYXJjaCksZmlsbD1hc19mYWN0b3IoU3Vydml2ZWQpKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KSsNCiAgbGFicyh4ID0gIk51bWJlciBvZiBwYXJlbnRzL2NoaWxkcmVuIix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKSArDQogIHRoZW1lX2lwc3VtX3JjKCkNCmdQYXJjaFN1cnZpdmVkDQpgYGANCg0KIyMjIEdlbmRlciBWUyBTdXJ2aXZlZA0KDQpgYGB7ciBmaWcuYWxpZ249J2NlbnRlcid9DQpnU2V4U3Vydml2ZWQgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChTZXgsU3Vydml2ZWQpICU+JQ0KICBnZ3Bsb3QoYWVzKGFzX2ZhY3RvcihTZXgpLGZpbGwgPSBhc19mYWN0b3IoU3Vydml2ZWQpKSkgKyANCiAgZ2VvbV9iYXIocG9zaXRpb24gPSAiZmlsbCIpICsNCiAgc2NhbGVfeV9jb250aW51b3VzKGxhYmVsID0gc2NhbGVzOjpwZXJjZW50KSArIA0KICBsYWJzKHggPSAiU2V4Iix5ID0gIlN1cnZpdmFsIFJhdGUiKSsNCiAgc2NhbGVfZmlsbF9kaXNjcmV0ZShuYW1lID0gIlN1cnZpdmVkIiwgbGFiZWxzID0gYygiRGlkbid0IFN1cnZpdmUiLCJTdXJ2aXZlZCIpKSArDQogIHRoZW1lX2lwc3VtX3JjKCkNCmdTZXhTdXJ2aXZlZA0KYGBgDQojIyMgU3Vydml2YWwgRGVuc2l0eSBhbmQgQWdlDQpgYGB7cn0NCmdTdXJ2aXZhbEFnZURlbnNpdHkgPC0gZ2dwbG90KHRyYWluWyghaXMubmEodHJhaW4kU3Vydml2ZWQpICYgIWlzLm5hKHRyYWluJEFnZSkpLF0sDQogICAgICAgICAgICBhZXMoeCA9IEFnZSwNCiAgICAgICAgICAgICAgICBmaWxsID0gU3Vydml2ZWQpKSArDQogIGdlb21fZGVuc2l0eShhbHBoYT0wLjUsDQogICAgICAgICAgICAgICBhZXMoZmlsbD1hc19mYWN0b3IoU3Vydml2ZWQpKSkgKw0KICBsYWJzKHRpdGxlPSJTdXJ2aXZhbCBkZW5zaXR5IGFuZCBBZ2UiKSArDQogIHNjYWxlX3hfY29udGludW91cyhicmVha3MgPSBzY2FsZXM6OnByZXR0eV9icmVha3MobiA9IDEwKSkgKw0KICB0aGVtZV9pcHN1bSgpDQoNCmdTdXJ2aXZhbEFnZURlbnNpdHkNCmBgYA0KDQojIyMgRGFzaGJvYXJkIG9mIHRoZSBwcmV2aW91cyBncmFwaHMNCg0KYGBge3J9DQpncmlkRXh0cmE6OmdyaWQuYXJyYW5nZShnUGNsYXNzU3Vydml2ZWQsDQogICAgICAgICAgICAgICAgICAgICAgICBnU2liU3BTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdTZXhTdXJ2aXZlZCwNCiAgICAgICAgICAgICAgICAgICAgICAgIGdQYXJjaFN1cnZpdmVkLA0KICAgICAgICAgICAgICAgICAgICAgICAgZ1N1cnZpdmFsQWdlRGVuc2l0eSwNCiAgICAgICAgICAgICAgICAgICAgICAgIG5yb3c9MykNCmBgYA0KDQpgYGB7cn0NCnRyYWluICU+JQ0KICBncm91cF9ieShTZXgpICU+JQ0KICBzdW1tYXJpc2UoQWdlX21lYW4gPSBtZWFuKEFnZSxuYS5ybT1UUlVFKSwNCiAgICAgICAgICAgIGFnZV9zZCA9IHNkKEFnZSxuYS5ybT1UKSwNCiAgICAgICAgICAgIHN1cml2YWxfbWVhbiA9IG1lYW4oU3Vydml2ZWQsbmEucm0gPVQpLA0KICAgICAgICAgICAgc3VyaXZhbF9zZCA9IHNkKFN1cnZpdmVkLG5hLnJtID0gVCkpDQoNCmBgYA0KDQojIyBTYW1wbGluZw0KDQojIyMgUmFuZG9tIHNhbXBsZSBvZiBzaXplIG9mIDUwIChBZ2UpDQoNCmBgYHtyIFNhbXBsaW5nLTF9DQpzYW1wbGVfNTAgPC0gc2FtcGxlX24odHJhaW4sc2l6ZSA9IDUwLHJlcGxhY2UgPSBUKSAlPiUNCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKEFnZSksIHNkID0gc2QoQWdlKSkNCnNhbXBsZV81MA0KYGBgDQojIyMgU2FtcGxpbmcgZGlzdHJ1YnV0aW9uIHdpdGggYSBmaXhlZCBzaXplICg1MCkgKG1lYW4pDQpgYGB7cn0NCm51bU9mU2FtcGxlcyA8LSBzZXEoNTAsMTAwMCw1MCkNCg0Kc21wbG5nRHN0cmJ0blJwc0NobmcgPC0gdGliYmxlKCkNCg0KZm9yKGkgaW4gbnVtT2ZTYW1wbGVzKXsNCiAgDQogIGZvcih5IGluIDE6aSl7DQogICAgbnNhbXBsZSA8LSBzYW1wbGVfbih0cmFpbixzaXplPTUwLHJlcGxhY2U9VCkgJT4lDQogICAgICBzZWxlY3QoQWdlKQ0KICAgIG5ld1JvdyA8LSBucm93KHNtcGxuZ0RzdHJidG5ScHNDaG5nKSArIDENCiAgICBzbXBsbmdEc3RyYnRuUnBzQ2huZ1tuZXdSb3csInJlcHMiXSA8LSBpDQogICAgc21wbG5nRHN0cmJ0blJwc0NobmdbbmV3Um93LCJ4X2JhciJdIDwtIG1lYW4obnNhbXBsZSRBZ2UsbmEucm0gPSBUKQ0KICB9DQogIA0KfQ0KDQpnU2FtcGxpbmdSZXBzIDwtIHNtcGxuZ0RzdHJidG5ScHNDaG5nICU+JQ0KICBwbG90X2x5KA0KICAgIHggPSB+eF9iYXIsDQogICAgZnJhbWUgPSB+cmVwcywNCiAgICB0eXBlID0gImhpc3RvZ3JhbSINCiAgKQ0KZ1NhbXBsaW5nUmVwcw0KYGBgDQoNCldoaWxlIG5vLiBvZiBzYW1wbGUgc2l6ZSBpbmNyZWFzZSB0aGUgdmFyaWFiaWxpdHkgb2Ygc2FtcGxpbmcNCmRpc3RyaWJ1dGlvbiBkZWNyZWFzZSBhbmQgdGhlIG1lYW4gaW5jcmVhc2UuDQoNCiMjIyBTYW1wbGluZyBkaXN0cmlidXRpb24gd2l0aCBhIGZpeGVkIHJlcGV0aXRpb25zIGFuZCBkaWZmZXJlbnQgc2l6ZXMgKG1lYW4pDQoNCmBgYHtyIHNhbXBsaW5nLWRpc3RydWJ1dGlvbi1zaXplc30NCnNpemVzIDwtIHNlcSgyMCwyNjAsMjApDQpzbXBsbmdEc3RyYnRuU3pDaG5nIDwtIHRpYmJsZSgpDQpmb3IoaSBpbiBzaXplcyl7DQogIGZvcih5IGluIDE6MTUwMCl7DQogICAgbnNhbXBsZSA8LSBzYW1wbGVfbih0cmFpbixzaXplPWkscmVwbGFjZT1UKSAlPiUNCiAgICAgIHNlbGVjdChBZ2UpDQogICAgbmV3Um93IDwtIG5yb3coc21wbG5nRHN0cmJ0blN6Q2huZykgKyAxDQogICAgc21wbG5nRHN0cmJ0blN6Q2huZ1tuZXdSb3csInNpemVzIl0gPC0gaQ0KICAgIHNtcGxuZ0RzdHJidG5TekNobmdbbmV3Um93LCJ4X2JhciJdIDwtIG1lYW4obnNhbXBsZSRBZ2UsbmEucm0gPSBUKQ0KICB9DQp9DQpnU2FtcGxpbmdTaXplIDwtIHNtcGxuZ0RzdHJidG5TekNobmcgJT4lDQogIHBsb3RfbHkoDQogICAgeCA9IH54X2JhciwNCiAgICBmcmFtZSA9IH5zaXplcywNCiAgICB0eXBlID0gImhpc3RvZ3JhbSINCiAgKQ0KZ1NhbXBsaW5nU2l6ZQ0KYGBgDQoNCldoaWxlIG5vLiBvZiBzYW1wbGUgc2l6ZSBpbmNyZWFzZSB0aGUgdmFyaWFiaWxpdHkgb2Ygc2FtcGxpbmcNCmRpc3RyaWJ1dGlvbiBkZWNyZWFzZSxhbmQgVGhlIHNhbXBsZSBkaXN0cmlidXRpb24gbWVhbiB3aWxsIGJlIG5vcm1hbGx5DQpkaXN0cmlidXRlZCBhcyBsb25nIGFzIHRoZSBzYW1wbGUgc2l6ZSBpcyBtb3JlIHRoYW4gMzAuDQoNCiMjIyBzYW1wbGluZyBkaXN0cmlidXRpb24gb2YgdmFyaWFuY2Ugd2l0aCBkaWZmZXJlbnQgc2l6ZXMgYW5kIGZpeGVkIHJlcGl0aW9ucy4NCg0KYGBge3J9DQpzaXplcyA8LSBjKDIsMjAsMjUsMzAsMzUsNDAsNDUsNTApDQpzbXBsbmdEc3RyYnRuU3pDaG5nVmFyaWFuY2UgPC0gdGliYmxlKCkNCmZvcihpIGluIHNpemVzKXsNCiAgZm9yKHkgaW4gMToxNTAwKXsNCiAgICBuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9aSxyZXBsYWNlPVQpICU+JQ0KICAgICAgc2VsZWN0KEFnZSkNCiAgICBuZXdSb3cgPC0gbnJvdyhzbXBsbmdEc3RyYnRuU3pDaG5nVmFyaWFuY2UpICsgMQ0KICAgIHNtcGxuZ0RzdHJidG5TekNobmdWYXJpYW5jZVtuZXdSb3csInNpemVzIl0gPC0gaQ0KICAgIHNtcGxuZ0RzdHJidG5TekNobmdWYXJpYW5jZVtuZXdSb3csInZhcmlhbmNlIl0gPC0gc2QobnNhbXBsZSRBZ2UsbmEucm0gPSBUKSoqMg0KICB9DQp9DQpnU2FtcGxpbmdTaXplVmFyaWFuY2UgPC0gc21wbG5nRHN0cmJ0blN6Q2huZ1ZhcmlhbmNlICU+JQ0KICBwbG90X2x5KA0KICAgIHggPSB+dmFyaWFuY2UsDQogICAgZnJhbWUgPSB+c2l6ZXMsDQogICAgdHlwZSA9ICJoaXN0b2dyYW0iDQogICkNCmdTYW1wbGluZ1NpemVWYXJpYW5jZQ0KYGBgDQoNCiMjIE1NRSBhbmQgTUxFDQoNCiQkIE1fMSA9IEUoeCkgPSBcZnJhY3sxfXtufSBcc3VtXntufV97aSA9IDF9IHhfaSAkJA0KJCQgTV8yID0gRSh4XjIpID0gXHNpZ21hXjIgKyAoRSh4KSleMiA9IFxmcmFjezF9e259IFxzdW1ee259X3tpID0gMX0ge3hfaX1eMiAkJA0KJCQgXG11ID0gXGZyYWN7MX17Mn0gXHN1bV57bn1fe2kgPTF9IHhfaSA9IE1fMSAkJA0KJCQgXHNpZ21hXjIgPSBFKHheMikgLSAoRSh4KSleMiA9IFxmcmFjezF9e259IFxzdW1ee259X3tpPTF9IHt4X2l9XjIgLSBcbGVmdCggXGZyYWN7MX17bn0gXHN1bV57bn1fe2kgPSAxfSB4X2kgXHJpZ2h0KV4yICQkDQoNCiMjIyBTaXplIDUwDQpgYGB7cn0NCiMgU2FtcGxpbmcNCm5fc2l6ZSA9IDUwDQpuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9bl9zaXplLHJlcGxhY2U9VCkNCiMgTU1FDQptZWFuX2VzdDE9c3VtKG5zYW1wbGUkQWdlKSAvIG5fc2l6ZQ0KdmFyX2VzdDEgPSAoc3VtKG5zYW1wbGUkQWdlXjIpIC8gbl9zaXplKSAtIChtZWFuX2VzdDEpXjINCm1lYW5fZXN0MQ0KdmFyX2VzdDENCmJpYXMoYWN0dWFsID0gbWVhbih0cmFpbiRBZ2UsbmEucm0gPSBUKSwgcHJlZGljdGVkID0gbWVhbl9lc3QxKQ0KIyBNTEUNCk5MTCA9IGZ1bmN0aW9uKHBhcnMsZGF0YSl7DQogICMgRXh0cmFjdCBwYXJhbWV0ZXJzIGZyb20gdGhlIHZlY3Rvcg0KICBtdSA9IHBhcnNbMV0NCiAgc2lnbWEgPSBwYXJzWzJdDQogIE5MTCA9IC1zdW0oZG5vcm0oeCA9IGRhdGEgLCBtZWFuID0gbXUsIHNkID0gc2lnbWEgLCBsb2cgPSBUUlVFKSkNCiAgIyBMb2cgb2YgcGRmDQogICMgbmVnYXRpdmUgdG8gZ2V0IG1heA0KfQ0KbWxlIDwtIG9wdGltKHBhciA9IGMobXU9IC4yICwgc2lnbWEgPSAxLjUpLGZuPU5MTCxkYXRhPW5zYW1wbGUkQWdlLGNvbnRyb2wgPSBsaXN0KHBhcnNjYWxlPWMobXU9IC4yICwgc2lnbWEgPSAxLjUpKSkNCm1sZSRwYXJbMV0NCg0KYmlhcyhhY3R1YWwgPSBtZWFuKHRyYWluJEFnZSxuYS5ybSA9IFQpLCBwcmVkaWN0ZWQgPSBtbGUkcGFyWzFdKQ0KYGBgDQoNCiMjIyBTaXplIDIwMA0KYGBge3J9DQojIFNhbXBsaW5nDQpuX3NpemUgPSAyMDANCm5zYW1wbGUgPC0gc2FtcGxlX24odHJhaW4sc2l6ZT1uX3NpemUscmVwbGFjZT1UKQ0KIyBNTUUNCm1lYW5fZXN0MT1zdW0obnNhbXBsZSRBZ2UpIC8gbl9zaXplDQp2YXJfZXN0MSA9IChzdW0obnNhbXBsZSRBZ2VeMikgLyBuX3NpemUpIC0gKG1lYW5fZXN0MSleMg0KbWVhbl9lc3QxDQp2YXJfZXN0MQ0KYmlhcyhhY3R1YWwgPSBtZWFuKHRyYWluJEFnZSxuYS5ybSA9IFQpLCBwcmVkaWN0ZWQgPSBtZWFuX2VzdDEpDQojIE1MRQ0KTkxMID0gZnVuY3Rpb24ocGFycyxkYXRhKXsNCiAgIyBFeHRyYWN0IHBhcmFtZXRlcnMgZnJvbSB0aGUgdmVjdG9yDQogIG11ID0gcGFyc1sxXQ0KICBzaWdtYSA9IHBhcnNbMl0NCiAgTkxMID0gLXN1bShkbm9ybSh4ID0gZGF0YSAsIG1lYW4gPSBtdSwgc2QgPSBzaWdtYSAsIGxvZyA9IFRSVUUpKQ0KICAjIExvZyBvZiBwZGYNCiAgIyBuZWdhdGl2ZSB0byBnZXQgbWF4DQp9DQptbGUgPC0gb3B0aW0ocGFyID0gYyhtdT0gLjIgLCBzaWdtYSA9IDEuNSksZm49TkxMLGRhdGE9bnNhbXBsZSRBZ2UsY29udHJvbCA9IGxpc3QocGFyc2NhbGU9YyhtdT0gLjIgLCBzaWdtYSA9IDEuNSkpKQ0KbWxlJHBhclsxXQ0KDQpiaWFzKGFjdHVhbCA9IG1lYW4odHJhaW4kQWdlLG5hLnJtID0gVCksIHByZWRpY3RlZCA9IG1sZSRwYXJbMV0pDQpgYGANCiMjIE1hbGUgQWdlIC0gRmVtYWxlIEFnZQ0KYGBge3J9DQphZ2VfbWFsZSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KEFnZSxTZXgpICU+JQ0KICBtdXRhdGUoU2V4ID0gYXNfZmFjdG9yKFNleCkpICU+JQ0KICBmaWx0ZXIoU2V4ID09ICJtYWxlIikNCmFnZV9mZW1hbGUgPC0gdHJhaW4gJT4lDQogIHNlbGVjdChBZ2UsU2V4KSAlPiUNCiAgbXV0YXRlKFNleCA9IGFzX2ZhY3RvcihTZXgpKSAlPiUNCiAgZmlsdGVyKFNleCA9PSAiZmVtYWxlIikNCg0Kc2FtcGxlX2FnZV9tYWxlXzUwIDwtIGFnZV9tYWxlICU+JQ0KICByZXBfc2FtcGxlX24oc2l6ZSA9IDUwLHJlcHMgPSAxNTAwMCxyZXBsYWNlID0gVCkgJT4lDQogIHN1bW1hcmlzZShhZ2VfbWFsZV9iYXIgPSBtZWFuKEFnZSxuYS5ybSA9IFQpKQ0Kc2FtcGxlX2FnZV9mZW1hbGVfNTAgPC0gYWdlX2ZlbWFsZSAlPiUNCiAgcmVwX3NhbXBsZV9uKHNpemUgPSA1MCxyZXBzID0gMTUwMDAscmVwbGFjZSA9IFQpICU+JQ0KICBzdW1tYXJpc2UoYWdlX2ZlbWFsZV9iYXIgPSBtZWFuKEFnZSxuYS5ybSA9IFQpKQ0KDQpzYW1wbGVkaWZmX21lYW5zIDwtIHNhbXBsZV9hZ2VfbWFsZV81MCRhZ2VfbWFsZV9iYXIgLSAgc2FtcGxlX2FnZV9mZW1hbGVfNTAkYWdlX2ZlbWFsZV9iYXIgJT4lDQogIGFzX3RpYmJsZSgpDQoNCmdzYW1wbGVkaWZmX21lYW5zIDwtIHNhbXBsZWRpZmZfbWVhbnMgJT4lDQogIGdncGxvdChhZXModmFsdWUsIHkgPSAuLmRlbnNpdHkuLikpICsNCiAgZ2VvbV9oaXN0b2dyYW0oYmlucyA9IDI1LGJpbndpZHRoID0gMSxjb2xvcj1pbmZlcm5vKDEsYWxwaGE9MSkpICsgDQogIGdlb21fZGVuc2l0eShmaWxsPWluZmVybm8oMSxiZWdpbiA9IDAuNSxhbHBoYSA9IDAuNSksY29sb3IgPSBpbmZlcm5vKDEsYmVnaW49MCkpICsNCiAgZ2d0aXRsZSgiRGlzdHJpYnV0aW9uIikgKw0KICB0aGVtZV9pcHN1bV9yYygpDQpnc2FtcGxlZGlmZl9tZWFucw0KYGBgDQoNCiMjIFN1cnZpdmVkIE1hbGUgLSBTdXJ2aXZlZCBGZW1hbGUNCg0KYGBge3J9DQpzdXJ2aXZlZF9tYWxlIDwtIHRyYWluICU+JQ0KICBzZWxlY3QoU3Vydml2ZWQsU2V4KSAlPiUNCiAgZmlsdGVyKFNleCA9PSAibWFsZSIpDQoNCnN1cnZpdmVkX2ZlbWFsZSA8LSB0cmFpbiAlPiUNCiAgc2VsZWN0KFN1cnZpdmVkLFNleCkgJT4lDQogIGZpbHRlcihTZXggPT0gImZlbWFsZSIpDQoNCnNhbXBsZV9zdXJ2aXZlX21hbGVfNTAgPC0gc3Vydml2ZWRfbWFsZSAlPiUNCiAgcmVwX3NhbXBsZV9uKHNpemUgPSA1MCxyZXBzID0gMTUwMDAscmVwbGFjZSA9IFQpDQpzYW1wbGVfc3Vydml2ZV9mZW1hbGVfNTAgPC0gc3Vydml2ZWRfZmVtYWxlICU+JQ0KICByZXBfc2FtcGxlX24oc2l6ZSA9IDUwLHJlcHMgPSAxNTAwMCxyZXBsYWNlID0gVCkNCnNhbXBsZWRpZmZfc3Vydml2ZWQgPC0gc2FtcGxlX3N1cnZpdmVfbWFsZV81MCRTdXJ2aXZlZCAtIHNhbXBsZV9zdXJ2aXZlX2ZlbWFsZV81MCRTdXJ2aXZlZCAlPiUgDQogIGFzX3RpYmJsZSgpDQoNCmdzYW1wbGVkaWZmX3N1cnZpdmVkIDwtIHNhbXBsZWRpZmZfc3Vydml2ZWQgJT4lDQogIGdncGxvdChhZXModmFsdWUpKSArDQogIGdlb21faGlzdG9ncmFtKGJpbnMgPSAyNSxiaW53aWR0aCA9IDEsY29sb3I9aW5mZXJubygxLGFscGhhPTEpKSArIA0KICBnZ3RpdGxlKCJEaXN0cmlidXRpb24iKSArDQogIHRoZW1lX2lwc3VtX3JjKCkNCmdzYW1wbGVkaWZmX3N1cnZpdmVkDQpgYGANCg0KVGhlIHBsb3QgaXMgYSBiaXQgaGFyZCB0byB1bmRlcnN0YW5kIHNvIGl0IG5lZWRzIGEgYml0IG9mIGV4cGxhbmF0aW9uLiAkLTEkIHJlcHJlc2VudHMgdGhlIGZlbWFsZSBzdXJ2aXZlZCwgJDAkIHJlcHJlc2VudHMgdGhlIG5laXRoZXIgb2YgdGhlbSBzdXJ2aXZlZCwgYW5kICQxJCBtZWFucyB0aGF0IHRoZSBtYWxlIHN1cnZpdmVkDQoNCkFmdGVyIGluc3BlY3RpbmcgdGhlIHBsb3QsIGl0IGJlY29tZXMgQ3J5c3RhbCBjbGVhciB0aGF0IHRoZXJlIHdhcyBhIGJpYXMgaW4gdGhlIHJlc2N1ZSBwcm9jZXNzIHdoZXJlIHJlc2N1ZXJzIHByZWZlcnJlZCB0byBzYXZlIGZlbWFsZSBtb3JlIHRoYW4gbWFsZXMuDQoNCiMjIENvbmZpZGVuY2UgSW50ZXJ2YWxzDQpgYGB7cn0NCm5fc2l6ZSA9IDEwDQpuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9IG5fc2l6ZSxyZXBsYWNlPVQpDQoNCiMgQ2FsY3VsYXRlIFRoZSBNZWFuDQptZWFuX2VzdDE9bWVhbihuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQoNCiMgQ2FsY3VsYXRlIFRoZSBTdGFuZGFyZCBEZXZpYXRpb24NCnNkX2VzdDE9c2QobnNhbXBsZSRBZ2UsbmEucm0gPSBUKQ0KDQojIENvbXB1dGluZyBUaGUgRXJyb3IgVXNpbmcgdGhlIHFub3JtKCkgRnVuY3Rpb24gdG8gQ2FsY3VsYXRlIFRoZSBOb3JtYWwgRGlzdHJpYnV0aW9uIA0KZXJyb3I9cXQoMC45NzUsZGYgPSA5LCBsb3dlci50YWlsID0gVCkqKHNkX2VzdDEvc3FydChuX3NpemUpKSAjIGF0IGxvd2VyLnRhaWwgPSBUIHByb2JhYmlsaXR5IGlzIFBbWDw9eF0gLCBvdGhlcndpc2UgUFtYPnhdDQoNCiNEZXRlcm1pbmluZyBUaGUgTWVhbiBJbnRlcnZhbFtdDQpsZWZ0IDwtIG1lYW5fZXN0MS1lcnJvcg0KcmlnaHQgPC0gbWVhbl9lc3QxK2Vycm9yDQoNCnByaW50KHBhc3RlMCgiWyIsbGVmdCwiLCIscmlnaHQsIl0iKSkNCmBgYA0KDQpgYGB7cn0NCm5fc2l6ZSA9IDUwDQpuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemU9bl9zaXplLHJlcGxhY2U9VCkNCg0KIyBDYWxjdWxhdGUgVGhlIE1lYW4NCm1lYW5fZXN0Mj1tZWFuKG5zYW1wbGUkQWdlLG5hLnJtID0gVCkNCg0KIyBDYWxjdWxhdGUgVGhlIFN0YW5kYXJkIERldmlhdGlvbg0Kc2RfZXN0Mj1zZChuc2FtcGxlJEFnZSxuYS5ybSA9IFQpDQoNCiMgQ29tcHV0aW5nIFRoZSBFcnJvciBVc2luZyB0aGUgcW5vcm0oKSBGdW5jdGlvbiB0byBDYWxjdWxhdGUgVGhlIE5vcm1hbCBEaXN0cmlidXRpb24gDQplcnJvcj1xbm9ybSgwLjk3NSkqKHNkX2VzdDEvc3FydChuX3NpemUpKQ0KDQojRGV0ZXJtaW5pbmcgVGhlIE1lYW4gSW50ZXJ2YWxbXQ0KbGVmdCA8LSBtZWFuX2VzdDItZXJyb3INCnJpZ2h0IDwtIG1lYW5fZXN0MitlcnJvcg0KDQpwcmludChwYXN0ZTAoIlsiLGxlZnQsIiwiLHJpZ2h0LCJdIikpDQpgYGANCiMjIE11bHRpcGxpY2F0aW9uIGFuZCBhZGRpdGlvbiBvZiBjb25zdGFudCB0byB0aGUgc2FtcGxlIHZhbHVlcw0KDQojIyMgTXVsdGlwbGljYXRpb24NCg0KYGBge3J9DQpuX3NpemUgPSAyMDANCg0KbnNhbXBsZSA8LSBzYW1wbGVfbih0cmFpbixzaXplID0gbl9zaXplLHJlcGxhY2UgPSBUKQ0KDQpuc2FtcGxlICU+JQ0KICBzdW1tYXJpc2UoQWdlX21lYW4gPSBtZWFuKEFnZSksIEFnZV9zZCA9IHNkKEFnZSksQWdlX3ZhciA9IHNkKEFnZSleMikNCg0KbnNhbXBsZSRBZ2UgPC0gbnNhbXBsZSRBZ2UgKjUNCm5zYW1wbGUgJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlKSwgQWdlX3NkID0gc2QoQWdlKSxBZ2VfdmFyID0gc2QoQWdlKV4yKQ0KDQpgYGANCiMjIyBBZGRpdGlvbg0KDQpgYGB7cn0NCm5fc2l6ZSA9IDIwMA0KDQpuc2FtcGxlIDwtIHNhbXBsZV9uKHRyYWluLHNpemUgPSBuX3NpemUscmVwbGFjZSA9IFQpDQoNCm5zYW1wbGUgJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlKSwgQWdlX3NkID0gc2QoQWdlKSxBZ2VfdmFyID0gc2QoQWdlKV4yKQ0KDQpuc2FtcGxlJEFnZSA8LSBuc2FtcGxlJEFnZSArIDUNCm5zYW1wbGUgJT4lDQogIHN1bW1hcmlzZShBZ2VfbWVhbiA9IG1lYW4oQWdlKSwgQWdlX3NkID0gc2QoQWdlKSxBZ2VfdmFyID0gc2QoQWdlKV4yKQ0KDQpgYGANCg0KIyBrZXJuZWwgZGlzdHJpYnV0aW9uDQoNCj4gQSBrZXJuZWwgZGlzdHJpYnV0aW9uIGlzIGEgbm9ucGFyYW1ldHJpYyByZXByZXNlbnRhdGlvbiBvZiB0aGUNCj4gcHJvYmFiaWxpdHkgZGVuc2l0eSBmdW5jdGlvbiAoJHBkZiQpIG9mIGEgcmFuZG9tIHZhcmlhYmxlIGluIGFueQ0KPiBwb3B1bGF0aW9uDQoNClRoZSBrZXJuZWwgc21vb3RoaW5nIGZ1bmN0aW9uIGRlZmluZXMgdGhlIHNoYXBlIG9mIHRoZSBjdXJ2ZSB1c2VkIHRvDQpnZW5lcmF0ZSB0aGUgcGRmIEtlcm5lbCBkaXN0cmlidXRpb24gaXMgUXVvdGUgZnJvbSBoaXN0b2dyYW0gaW4gb3RoZXINCndvcmQgKHNtb290aCByZXByZXNlbnRhdGlvbiBvZiBhIGhpc3RvZ3JhbSkgVGhhdCB0aGUgaW50ZWdyYWwgPTEgVGhlcmUNCmlzIGEgYmVuZWZpdCBvZiBzbW9vdGggcmVwcmVzZW50YXRpb24gb2YgYSBoaXN0b2dyYW0gbGlrZSBJZ25vcmVzDQppcnJlZ3VsYXJpdGllcyBhbmQgb3V0bGllcnMgLCBtb3JlIGVmZmljaWVudCBpbiBhcHByb3hpbWF0aW9uIHNvIGl0DQpkZWFscyBiZXR0ZXIgd2l0aCBsYXJnZSBkYXRhIHRoYW4gc21hbGwgZGF0YQ0KDQokJCBcaGF0e2ZfaH0gPSBcZnJhY3sxfXtufSA9IFxzdW1ebl97aSA9IDF9IEsoeC14X2kpICA9IFxmcmFjezF9e25ofSBLXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSAkJA0KDQojIyBSdWxlcw0KDQojIyMgTm9uLXdlaWdodGVkIERhdGENCg0KJCQgXGhhdHtmX2h9ID0gXGZyYWN7MX17bn0gXHN1bV5uX3tpID0gMX0gSyh4LXhfaSkgID0gXGZyYWN7MX17bmh9IEtcbGVmdChcZnJhY3t4LXhfaX17aH1ccmlnaHQpICQkDQoNCiMjIyBXZWlnaHRlZCBEYXRhDQoNCiQkDQpcaGF0e2ZfaH0gPSBcZnJhY3sxfXtofSBcc3VtXk5fe2k9MX0gd19pIEsgXGxlZnQoXGZyYWN7eC14X2l9e2h9XHJpZ2h0KSwgXHFxdWFkIFx0ZXh0e3doZXJlfSBcc3VtXk5fe2k9IDF9IHdfaSAgPSAxDQokJA0KDQojIyBLZXJuZWwgRnVuY3Rpb24NCg0KMS4gQm94DQoyLiBUcmlhbmdsZQ0KMy4gTm9ybWFsDQo0LiBQYW50ZWNobmljb24NCg0KRWFjaCBkZW5zaXR5IGN1cnZlIHVzZXMgdGhlIHNhbWUgaW5wdXQgZGF0YSwgYnV0IGFwcGxpZXMgYSBkaWZmZXJlbnQNCmtlcm5lbCBzbW9vdGhpbmcgZnVuY3Rpb24gdG8gZ2VuZXJhdGUgdGhlIHBkZi4gVGhlIGRlbnNpdHkgZXN0aW1hdGVzIGFyZQ0Kcm91Z2hseSBjb21wYXJhYmxlLCBidXQgdGhlIHNoYXBlIG9mIGVhY2ggY3VydmUgdmFyaWVzIHNsaWdodGx5LiBGb3INCmV4YW1wbGUsIHRoZSBib3gga2VybmVsIHByb2R1Y2VzIGEgZGVuc2l0eSBjdXJ2ZSB0aGF0IGlzIGxlc3Mgc21vb3RoDQp0aGFuIHRoZSBvdGhlcnMuDQoNClRoZSBjaG9pY2Ugb2YgYmFuZHdpZHRoIHZhbHVlIGNvbnRyb2xzIHRoZSBzbW9vdGhuZXNzIG9mIHRoZSByZXN1bHRpbmcNCnByb2JhYmlsaXR5IGRlbnNpdHkgY3VydmUgKGhpZ2hlciB2YWx1ZSBvZiAkaCQgbW9yZSBzbW9vdGhpbmcpDQoNClNwZWNpZnlpbmcgYSBzbWFsbGVyIGJhbmR3aWR0aCBwcm9kdWNlcyBhIHZlcnkgcm91Z2ggY3VydmUsIGJ1dCByZXZlYWxzDQp0aGF0IHRoZXJlIG1pZ2h0IGJlIHR3byBtYWpvciBwZWFrcyBpbiB0aGUgZGF0YS4gU3BlY2lmeWluZyBhIGxhcmdlcg0KYmFuZHdpZHRoIHByb2R1Y2VzIGEgY3VydmUgbmVhcmx5IGlkZW50aWNhbCB0byB0aGUga2VybmVsIGZ1bmN0aW9uDQpDaG9vc2luZyB0aGUgb3B0aW1hbCAoJGgkKSBiYW5kd2lkdGggbWV0aG9kcyA6DQoNCjEuIFNpbHZlcm1hbidzIHJ1bGUgb2YgdGh1bXAgdGhhdCBjb21wdXRlcyBhbiBvcHRpbWFsIGggYnkgYXNzdW1pbmcNCiAgICB0aGF0IGRhdGEgaXMgbm9ybWFsbHkgZGlzdHJpYnV0ZWQNCjIuIEltcHJvdmVkIFNoZWF0aGVyIEpvbmVzIChJU0opIGFuIGFsZ29yaXRobSBpcyBtb3JlIHJvYnVzdCB3aXRoDQogICAgbXVsdGltb2RhbGl0eSBkYXRhIG9yIGEgbG90IG9mIGRhdGEgKG9uZSBkaXNhZHZhbnRhZ2UgaXMgaXQgbmVlZHMgdG8NCiAgICBsYXJnZSBkYXRhICkNCg0KQm91bmRlZCBkb21haW5zIGRhdGE6IGhhdmUgYSBjb25zdHJhaW5zIGxpa2UgZGF0YSBjb3VsZG4ndCBiZSBuZWdhdGl2ZSAoDQotdmUgbGVhZCB0byBwcm9iYWJpbGl0eSA9IDApDQoNCiMjIE1pcnJvciBtZXRob2QNCg0KMS4gIE1pcnJvciB0aGUgZGF0YQ0KMi4gIFN1bSB0aGUgb3JpZ2luYWwgYW5kIG1pcnJvcmVkIGtlcm5lbCBkZW5zaXR5IGVzdGltYXRlDQozLiAgQ2hvcCBpdCBzbyB0aGF0IHplcm8gYXQgdGhlIGJvdW5kYXJ5IHNpZGUNCg0KIyMgMkQNCg0KJGgkIDogY291bGQgYmUgbWF0cml4IChkaWZmZXJlbnQgJGgkIGluIGRpZmZlcmVudCBkaXJlY3Rpb25zKQ0KDQpUaGUgY2hvaWNlIG9mIG5vcm0gY29tZXMgaW50byAkZCBcZ2UgMiQNCg0KVGhlIHAtbm9ybSBpcyAkfHx4fHxfcCA6PSAoXHN1bV97aT0xfSB8eHxecClee1xmcmFjezF9e3B9fSQgLSBub3JtLXAgPTENCk1hbmhhdHRhbiBkaXN0YW5jZSAtIG5vcm0tcCA9MiBFdWNsaWRlYW4gbm9ybSAtIG5vcm0tcCA9aW5mIG1heGltdW0gbm9ybQ0KKGl0J3Mgbm90IG9idmlvdXMgaW4gZXZlcnkgY2FzZSB3aGljaCBub3JtIGlzIHRoZSBjb3JyZWN0IG9uZSkNCg0Kc3RhbmRhcmQgZXVjbGlkZWFuIGRpc3RhbmNlIGlzIGdvb2QgY2hvaWNlIGJlY2F1c2UgaXQgaW52YXJpYW50IHVuZGVyDQpyb3RhdGlvbiBhcyBsYXJnZSBkYXRhIGNob2ljZSBvZiBrIGFuZCBwIGlzbid0IGltcG9ydGFudCBzbw0KDQojIEtvbG1vZ29yb3YtU21pcm5vdiBUZXN0DQoNCj4gTm9uIHBhcmFtZXRyaWMgdGVzdA0KDQpLb2xtb2dvcm92LVNtaXJub3YgVGVzdCBpcyB1c2VkIHRvOg0KMS4gRGVjaWRlIGlmIGEgc2FtcGxlIGNvbWVzIGZyb20gYSBwb3B1bGF0aW9uIHdpdGggYW4gZXhwZWN0ZWQgY29udGludW91cyBkaXN0cmlidXRpb24gKG1vc3RseSBub3JtYWwgZGlzdHJpYnV0aW9uKQ0KMi4gVG8gdGVzdCBmb3IgdGhlIGRpZmZlcmVuY2UgaW4gdGhlIHNoYXBlIG9mIHR3byBzYW1wbGUgZGlzdHJpYnV0aW9ucw0KDQohW10oaW1nL2tzLTEucG5nKQ0KDQpDb21wYXJlIG92ZXJhbGwgc2hhcGUgb2YgZGlzdHJpYnV0aW9uLCBub3Qgc3BlY2lmaWNhbGx5IHBhcmFtZXRlcg0KDQpLb2xtb2dvcm92LVNtaXJub3YgVGVzdCBpcyBkZWZpbmVkIGJ5IGEgaHlwb3RoZXNpczogDQokSF8wJCA6IHRoZSBkYXRhIGZvbGxvdyBzcGVjaWZpYyBkaXN0cmlidXRpb24gJEYoeCkgPSBGX1QgKHgpJA0KJEhfYSQgOiB0aGUgZGF0YSBkb24ndCBmb2xsb3cgc3BlY2lmaWMgZGlzdHJpYnV0aW9uICAkRih4KSBcbmVxIEZfVCAoeCkkDQoNCktTLXRlc3QgaXMgbWFkZSBiZXR3ZWVuIHNvbWUgdGhlb3JldGljYWwgY3VtdWxhdGl2ZSBkaXN0cmlidXRpb24gZnVuY3Rpb24gKCRGX1QoeCkkKSwgYW5kIGEgc2FtcGxlIGN1bXVsYXRpdmUgZGlzdHJpYnV0aW9uIGZ1bmN0aW9uICgkRl9zKHgpJCkgdGhhdCBtZWFzdXJlZCBieSB0aGUgc3RhdGlzdGljIEQsIHdoaWNoIGlzIHRoZSBncmVhdGVzdCB2ZXJ0aWNhbCBkaXN0YW5jZSBiZXR3ZWVuIHRoZW0uDQokJCBEID0gXHVuZGVyYnJhY2V7XHRleHR7c3VwfX1fe3h9IHwgRl9zKHgpIC0gRl90ICh4KXwkJA0KIVtdKGltZy9rcy0yLnBuZykNCg0KMS4gRGV0ZXJtaW5lICR4JCB2YWx1ZXMNCjIuIERldGVtcmluZSBmcmVxdWVuY3kgb2YgZWFjaCBvYnNlcnZhdGlvbg0KMy4gQ2FsY3VsYXRlIGN1bXVsYXRpdmUgZnJlcXVlbmN5DQo0LiBDYWxjdWxhdGUgJEZfcyh4KSBccmlnaHRhcnJvdyBcZnJhY3tcdGV4dHtjdW11bGF0aXZlIGZyZXF1ZW5jZX19e059JCB3aGVuICRuID4gMzAkICgkeiQtc2NvcmVzKSwgd2hlbiAkbjwzMCQgKCR0JC1zY29yZXMpDQo1LiBjYWxjdWxhdGUgJEZfdCh4KSQNCjYuIENhbGN1bGF0ZSAkRCBccmlnaHRhcnJvdyBGX3MoeCkgLSBGX1QoeCkkDQo3LiBDaG9vc2UgbWF4aW11bSAkRCQNCjguIENhbGN1bGF0ZSAkUCQtdmFsdWUNCjkuIERldGVybWluZSBLb2xtb2dvcm92J3MgcXVhcnRpbGUNCjEwLiBDb21wYXJlICRwJC12YWx1ZSB3aXRoIEtvbG1vZ29yb3ZlJ3MgcXVhcnRpbGU=